#include "HiranipraCommon.h"
#include "PhysFSCommon.h"
#include "PhysFSSystem.h"
#include "PhysFSStream.h"

#include "physfs.h"

using namespace Hiranipra;
using namespace Hiranipra::HCM;
using namespace Hiranipra::Platform;
using namespace Hiranipra::Platform::IO;

PhysFSStream::PhysFSStream( const PHYSFS_File* handle, bool readOnly ) :
	_handle( ( PHYSFS_File* )handle ), _isOpen( true ), _readOnly( readOnly )
{
}

PhysFSStream::~PhysFSStream()
{
	if( _isOpen == true )
		this->Close();
}

int64 PhysFSStream::GetLength() const
{
	if( _isOpen == false )
		return -1;
	return PHYSFS_fileLength( _handle );
}

void PhysFSStream::SetBuffer( const uint32 bufferSize )
{
	if( _isOpen == false )
		return;
	PHYSFS_setBuffer( _handle, bufferSize );
}

void PhysFSStream::Flush()
{
	if( _isOpen == false )
		return;
	if( _readOnly == true )
		return;
	PHYSFS_flush( _handle );
}

void PhysFSStream::Close()
{
	if( _isOpen == false )
		return;
	_isOpen = false;
	PHYSFS_close( _handle );
	_handle = NULL;
}

int64 PhysFSStream::GetPosition() const
{
	if( _isOpen == false )
		return -1;
	return PHYSFS_tell( _handle );
}

bool PhysFSStream::SetPosition( const int64 position )
{
	if( _isOpen == false )
		return false;
	return PHYSFS_seek( _handle, position ) > 0;
}

bool PhysFSStream::IsEOF() const
{
	if( _isOpen == false )
		return true;
	return PHYSFS_eof( _handle ) > 0;
}

uint32 PhysFSStream::Read( void* buffer, uint32 size )
{
	if( _isOpen == false )
		return 0;
	int64 read = PHYSFS_read( _handle, buffer, 1, size );
	return ( uint32 )read;
}

uint32 PhysFSStream::Write( const void* buffer, uint32 size )
{
	if( _isOpen == false )
		return 0;
	if( _readOnly == true )
		return 0;
	int64 wrote = PHYSFS_write( _handle, buffer, 1, size );
	return ( uint32 )wrote;
}

uint32 PhysFSStream::Read( MemoryBuffer* buffer, uint32 toRead )
{
	if( _isOpen == false )
		return 0;
	void* ptr = buffer->Lock();
	if( ptr == NULL )
		return 0;
	int64 remaining = this->GetLength() - this->GetPosition();
	uint32 realToRead = ( uint32 )MIN( toRead, remaining );
	if( buffer->Resize( realToRead ) == false )
		return 0;
	ptr = buffer->GetPointer();
	int64 read = PHYSFS_read( _handle, ptr, 1, realToRead );
	buffer->Unlock();
	return ( uint32 )read;
}

uint32 PhysFSStream::Write( MemoryBuffer* buffer )
{
	if( _isOpen == false )
		return 0;
	if( _readOnly == true )
		return 0;
	uint32 size;
	void* ptr = buffer->Lock( &size );
	if( ptr == NULL )
		return 0;
	int64 wrote = PHYSFS_write( _handle, ptr, 1, size );
	buffer->Unlock();
	return ( uint32 )wrote;
}

bool PhysFSStream::ReadAll( MemoryBuffer& buffer )
{
	buffer.Lock();
	bool resized = buffer.Resize( ( uint32 )this->GetLength() );
	if( resized == false )
	{
		buffer.Unlock();
		return false;
	}
	void* ptr = buffer.GetPointer();
	bool read = this->Read( ptr, buffer.GetSize() ) > 0;
	buffer.Unlock();
	return read;
}
